import os.path
import logging
import yaml

# # Editor页面的 title-btns 按该列表排序
# PREDEFINE_IOTYPE = ["PLC", "CNC", "集成电柜", "SIM"]  # 对应iotype.yaml中的name项

def traverse_nodes(dict_node):
    """ return a set collection of Type_Node """
    set_node = set()
    for type_key, type_value in dict_node.items():
        set_node.add(Type_Node(type_key, type_value))
    return set_node


class Type_Node:
    def __init__(self, type_key: str, type_value: dict):
        self.key = type_key  # like "CNC"
        self.prefix = None
        self.ui_name = None  # 用于显示在IO-Editor模式页面
        self.sub_type = None  # set() of sub-Type_Node
        if type_value:
            self.set_type_value(type_value)

    def set_type_value(self, dict_value):
        if "name" in dict_value:
            self.ui_name = dict_value["name"]
        if "prefix" in dict_value:
            self.prefix = dict_value["prefix"]
        if "sub_type" in dict_value:
            self.sub_type = traverse_nodes(dict_value["sub_type"])


class IO_Type:
#     def __init__(self):
#         self.set_node = set()
#         self.dict_prefix = {}
# """
# {'PLC': {'IX': None, 'MB': None, 'MD': None, 'QX': None},
#  'cnc': None,
#  'pdb': None,
#  'spdslct': None,
#  'tool': None}
# """
#         self.dict_name = {}
# """
# {'活跃的PLC': {'我是可怜滴PLC M区域': None, '输入': None, '输出': None},
#  '测试版CNC': {'1_cnc': None, '2_pdb': None, '3_tool': None, '4_spdslct': None}}
# """
#         self.dict_name2prefix = {}
# """
# {'cnc_name': ['cnc'],
#  'pdb_name': ['pdb'],
#  'tool_name': ['tool'],
#  'spdslct_name': ['spdslct'],
#  '我是可怜滴PLC M区域': ['MB', 'MD'],
#  '活跃的PLC': ['PLC'],
#  '测试版CNC': ['cnc', 'tool', 'pdb', 'spdslct'],
#  '输入': ['IX'],
#  '输出': ['QX']}
# """

    def load_from_file(self, yaml_path: str):
        with open(yaml_path, "r") as fp:
            yaml_data = yaml.load(fp)

        self.set_node = traverse_nodes(yaml_data)
        self._extract_prefix_tree()
        self._extract_name_tree()
        self._extract_name2prefix()

    def _extract_prefix_tree(self):
        """ this function will make self.dict_prefix """
        def recurve_extract(set_node):
            """ return a dict of prefix of current dict_node"""
            dict_node_prefix = {}
            for type_node in set_node:
                dict_sub_node_prefix = None  # 如不存在子项，则为None值
                if type_node.sub_type:
                    dict_sub_node_prefix = recurve_extract(type_node.sub_type)  # 递归处理

                logging.debug("IO_Type::_extract_prefix_tree() -->> node_key:{}, \
                               node_prefix:{}".format(type_node.key, type_node.prefix))
                if type_node.prefix:  # prefix 不为空
                    dict_node_prefix[type_node.prefix] = dict_sub_node_prefix
                else:
                    # 若父项的prefix为空，则prefix层级提升
                    logging.debug("IO_Type::_extract_prefix_tree() -->> node_prefix:{}, \
                                   sub_node_prefix:{}".format(type_node.prefix, dict_sub_node_prefix))
                    dict_node_prefix = dict(dict_node_prefix, **dict_sub_node_prefix)  # 组合字典
            return dict_node_prefix

        self.dict_prefix = recurve_extract(self.set_node)
        logging.debug("IO_Type::_extract_prefix_tree() -->> self.dict_prefix={}".format(self.dict_prefix))

    def _extract_name_tree(self):
        """ this function will make self.dict_name """
        def recurve_extract(set_node):
            """ 不同于_extract_prefix_tree(), 若父项ui_name为空，则不再检查子项 """
            dict_node_name = {}
            for type_node in set_node:
                if not type_node.ui_name:  continue

                # dict_node_name[type_node.ui_name] = None
                dict_sub_node_name = None
                if type_node.sub_type:
                    dict_sub_node_name = recurve_extract(type_node.sub_type)  # 递归处理
                    if not dict_sub_node_name:  # 若返回值为空字典
                        dict_sub_node_name = None

                dict_node_name[type_node.ui_name] = dict_sub_node_name
            return dict_node_name

        self.dict_name = recurve_extract(self.set_node)
        logging.debug("IO_Type::_extract_type_tree() -->> self.dict_name={}".format(self.dict_name))

    def _extract_name2prefix(self):
        """ this function will make self.dict_name2prefix """
        # 类似_extract_name_tree(), 若父项ui_name为空，则不再检查子项
        self.dict_name2prefix = {}
        def recurve_extract(set_node):
            # 类似_extract_name_tree(), 若父项ui_name为空，则不再检查子项
            for type_node in set_node:
                if not type_node.ui_name:  continue

                def recurve_extract_prefix(node):
                    """ return a LIST of prefix """
                    # 若存在对应的prefix
                    if node.prefix:
                        return [node.prefix]
                    elif not node.sub_type:
                        # 要么存在对应prefix，要么sub_type中存在可提取的prefix，否则ui-name的定义无意义
                        raise Exception("IO_Tyep::_extract_name2prefix() 没有【{}】对应的prefix数据".format(node.ui_name))
                    else:  # 否则，将子类的prefix作为list_value
                        list_node_prefix = []
                        for sub_node in node.sub_type:
                            list_node_prefix.extend(recurve_extract_prefix(sub_node))
                        return list_node_prefix

                self.dict_name2prefix[type_node.ui_name] = recurve_extract_prefix(type_node)

                # 递归处理sub_type
                if type_node.sub_type:
                    recurve_extract(type_node.sub_type)

        recurve_extract(self.set_node)
        logging.debug("IO_Type::_extract_type_tree() -->> self.dict_name2prefix={}".format(self.dict_name2prefix))

    def check_type(self, prefix: str):
        """ return bool for OK or not """
        try:
            list_prefix = prefix.split('.')
            if not list_prefix[-1]:
                list_prefix = list_prefix[0:-1]  # 清除空白尾缀【"cnc."】
            current_level_prefix = self.dict_prefix
            for ui_name in list_prefix:
                if ui_name not in current_level_prefix:
                    return False
                current_level_prefix = current_level_prefix[ui_name]
        except TypeError:
            return False
        return True

IO_TYPE = IO_Type()  # 定义全局变量


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    io_type = IO_Type()
    io_type.load_from_file("test/iotype.yaml")

    # 检测prefix的合法性
    check_prefix = "cnc."
    print("PREFIX[{}] is".format(check_prefix), "DEFINED" \
           if io_type.check_type(check_prefix) else "not defined.")

    import pprint
    pp = pprint.PrettyPrinter(indent=1)
    pp.pprint(io_type.dict_prefix)
    pp.pprint(io_type.dict_name)
    pp.pprint(io_type.dict_name2prefix)
